package com.mhy.hotfix;

import android.app.Application;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 基于Android8.0   SDK26
 */
public class Hotfix {
    /**
     * 私有目录 data/hotfix/xx.dex
     */
    public static void installPatch(Application application, File patch){
        if (!patch.exists()){
            return;
        }
        //获取当前应用的PathClassLoader
        ClassLoader classLoader = application.getClassLoader();

        try {
            //反射获取到DexPathList属性对象pathList;
            Field field = ReflectUtils.findField(classLoader, "pathList");
            Object pathList = field.get(classLoader);


            //3.1、把补丁包patch.dex转化为Element[]  (patch)
            Method method = ReflectUtils.findMethod(pathList, "makeDexElements", List.class, File.class, List.class, ClassLoader.class);
            //构建第一个参数
            List patchs = new ArrayList();
            patchs.add(patch);
            //构建第三个参数
            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
            //执行
            Object[] patchElements = (Object[]) method.invoke(null, patchs, null, suppressedExceptions, classLoader);


            //3.2获得pathList的dexElements属性（old）
            Field dexElementsField = ReflectUtils.findField(pathList, "dexElements");
            Object[] dexElements = (Object[]) dexElementsField.get(pathList);

            //3.3、patch+old合并，并反射赋值给pathList的dexElements
            Object[] newElements = (Object[]) Array.newInstance(patchElements.getClass().getComponentType(), patchElements.length + dexElements.length);
            System.arraycopy(patchElements, 0, newElements,0, patchElements.length);
            System.arraycopy(dexElements,0,newElements,patchElements.length, dexElements.length);
            dexElementsField.set(pathList, newElements);


        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}